01 Jun 2025

feedSymfony Blog

A Week of Symfony #961 (May 26 – June 1, 2025)

This week, Symfony released the stable version of Symfony 7.3, which includes lots of amazing new features. We also published the maintenance versions 6.4.22 and 7.2.7. Symfony development highlights This week, 67 pull requests were merged (34 in code and…

01 Jun 2025 7:08am GMT

30 May 2025

feedDrupal.org aggregator

jofitz: Drupal AI: first steps

Artificial Intelligence has arrived in the Drupal ecosystem and it is already making huge waves. This is the first in a series of articles about my experiences as I dip a toe into these exciting waters.

The task

I was working on a large website with plenty of content dating back years, including numerous product reviews that required improved categorisation. The goal was to tag all of these review nodes with the relevant Make and Model.

The approach

I chose to write a custom Drush script to loop through all of the review nodes, using Artificial Intelligence to parse the Title and Body fields and return the make and model discussed therein.

public function reviewsMakeModel(): void {
  $fields = ['title', 'body'];
  foreach ($review_nodes as $review_node) {
    $data = [];
    foreach ($fields as $field) {
      $data[] = $review_node->get($field)->value;
    }
    $text = implode("\n", $data);
    $makeAndModel = $this->getMakeAndModel($text);
    
    // More to follow...
  }
}

The provider

I...

Read more

30 May 2025 3:43pm GMT

Four Kitchens: Creating depth and motion: A step-by-step guide to parallax

Crafting a visually engaging website isn't just about eye-catching colors and typography - today it's also about creating immersive experiences that captivate users as they scroll. One of the most compelling ways to achieve this is by using a parallax effect, where elements move at different speeds to create a sense of depth and motion.

With a thoughtful approach and some JavaScript, you can seamlessly add this effect to your site, enhancing storytelling and making your pages more dynamic.

This post will guide you through the process of integrating a custom parallax effect into your site. Whether building a feature-rich landing page or enhancing storytelling elements, this technique can bring your site to life. Let's begin.

Building our component

Our example site was built in Drupal, but the overall concept would be the same in any CMS. First, we will need to build a component that has all the necessary fields that we want to display in our parallax. In this example, we will use Paragraph types and have two kinds of slides: one with an image and another without an image.

Parallax image slide

This slide will let us add an image, a title, and the caption or information we want to tell about that specific slide, the alignment of the information (left, center, or right), and an option if we want to hide the credit of the image or show it.

parallax image slide
Parallax blank slide

This slide is similar to the previous one, but there are key differences. This one won't include an image and anything else related to the image, and we allow a lot more text formatting on blank slides. This means that we can have the text on a blank slide take up much more of the available space without worrying about color contrast issues with advanced text formatting.

parallax blank slide

Once both paragraphs have been created, let's create a 'Parallax Slideshow' paragraph that will only have a field that references the previous paragraphs created.

slide items

Connecting our component to the custom theme

Once our component is ready, the next step is to integrate it into our custom theme. In this example, we're using Emulsify, our design system, as our custom theme, to ensure a consistent and modular approach to theming.

First, we will have our paragraph--parallax-slideshow.html.twig that will include a parallax-slideshow.twig, which has a JavaScript library called parallax-slideshow that is in charge of all logic to make our parallax effect work, and also some required styles.

{{ attach_library('your_theme/parallax-slideshow') }}

{% set drupal = true %}

{% include "@organisms/parallax-slideshow/parallax-slideshow.twig"
  with {
    'slideshow_id': paragraph.id.0.value,
    'slides': content.field_slide_items|render
  }
%}

Here's what our parallax-slideshow.twig looks like. Notice the empty <div class=""parallax-slideshow__image-wrapper""></div>. This is where the slide images will be rendered and where the fade-in and fade-out effects between images will occur.

{%
  set classes = [
  paragraph.bundle|clean_class,
  "parallax-slideshow",
]
%}

<div{{ attributes.addClass(classes) }} data-id="{{ slideshow_id }}">
  <div class="parallax-slideshow__wrapper">
    <div class="parallax-slideshow__image-wrapper"></div>
    {{ slides }}
  </div>
</div>

Then, we will have a paragraph--parallax-image-slide.html.twig and a paragraph--parallax-blank-slide.html.twig. Both files include a parallax-slide.twig, which is a molecule in the design system that organizes the content of each slide and adds all the needed styles. They are almost identical - the only difference being that the blank-slide will not pass the slide image to the parallax-slide.twig file.

{% include "@molecules/parallax-slide/parallax-slide.twig" with {
  'slide_id': paragraph.id.0.value,
  'slide_img': content.field_image|render,
  'slide_title': paragraph.field_component_title.0.value,
  'slide_caption': content.field_caption|render,
  'slide_caption_alignment': paragraph.field_caption_alignment.0.value,
  'slide_hide_credit': paragraph.field_hide_credit.0.value,
  'slide_type': paragraph.type.0.value.target_id,
} %}

Here's what our parallax-slide.twig looks like:

{%
  set classes = [
  'parallax-slide',
  slide_caption_alignment ? 'parallax-slide--caption-' ~ slide_caption_alignment|lower : '',
  slide_type ? 'parallax-slide--' ~ slide_type|replace({'_': '-'}) : '',
]
%}

<div {{ attributes.addClass(classes) }} slide-data-id="{{ slide_id }}">
    <div class="parallax-slide__info-wrapper">
      <div class="parallax-slide__info-inner-wrapper full-width">
        {% if slide_title %}
          <div class="parallax-slide__title-wrapper">
            <h1 class="parallax-slide__title">{{ slide_title }}</h1>
          </div>
        {% endif %}
        {% if slide_caption %}
          <div class="parallax-slide__caption">{{ slide_caption }}</div>
        {% endif %}
      </div>
    </div>
</div>

Preloading parallax slideshow data

To prevent a visible delay between slides, the component needs to preload the first two images on page load. As the user begins scrolling, additional images are loaded dynamically in the background. This ensures a seamless transition between slides without noticeable lag and enhances the overall user experience.

We need to pass structured data from the backend to JavaScript. Below is a function that loads the data and attaches it to drupalSettings for use in a theme.

function your_theme_preprocess_paragraph_parallax_slideshow(&$variables) {
  $paragraph = $variables['paragraph'];
  $pid = $paragraph->id();
  $lazy_load_data[$pid] = [];
  if ($paragraph->hasField('field_slide_items')) {
    $slide_items_ref = $paragraph->get('field_slide_items');
    $slide_items = $slide_items_ref->referencedEntities();

    foreach ($slide_items as $slide_id => $slide) {

      // Initial setup of array.
      $lazy_load_data[$pid][$slide_id] = [
        'id' => NULL,
        'image' => NULL,
      ];

      // ID.
      if ($slide->hasField('id') && !$slide->get('id')->isEmpty()) {
        $lazy_load_data[$pid][$slide_id]['id'] = $slide->get('id')->first()->getValue();
      }

      // Responsive image markup.
      if ($slide->hasField('field_image') && !$slide->get('field_image')->isEmpty()) {
        $lazy_load_data[$pid][$slide_id]['image'] = _your_theme_get_rendered_slide_image($slide);
      }
    }
  }

// Attach to JS JSON object to read in theme.
$variables['#attached']['drupalSettings']['yourTheme']['parallaxSlideshowData'] = $lazy_load_data;
  $variables['#attached']['library'][] = 'your_theme/parallax-slideshow';
}
function your_theme_get_rendered_slide_image($slide) {
  if ($slide->hasField('field_image') && !$slide->get('field_image')->isEmpty()) {
    $image_view = $slide->field_image->view('default');
    $rendered_image = \Drupal::service('renderer')->render($image_view);
    return $rendered_image;
  }

  return NULL;
}

Once the data is attached to drupalSettings in our JavaScript file, we can access parallaxSlideshowData to dynamically load images and control the parallax effect.

JavaScript implementation of the parallax slideshow

Below is a breakdown of how the JavaScript file works to bring the parallax slideshow to life.

Drupal.behaviors.parallaxSlideshow = {
    attach: function (context) {
      const parallaxSlideshowData =
        drupalSettings.yourTheme.parallaxSlideshowData;
      if (!parallaxSlideshowData) return;
      const slideshows = once('parallax-slideshow', '.parallax-slideshow', context);
      slideshows.forEach((slideshow) => {
        const loadedSlideIds = new Set();
        const loadedImages = new Set();
        initializeParallaxSlideshow(slideshow, parallaxSlideshowData, loadedSlideIds, loadedImages);
      });
    },
  };

Let's start by retrieving the slideshow data from drupalSettings and ensuring the script only runs once per slideshow element. The function initializeParallaxSlideshow is responsible for setting up and managing the parallax slideshow experience by initializing each slideshow. By tracking which slides have been loaded, we prevent redundant loading:

const slideshowDataID = slideshow.getAttribute('data-id');
const slideshowData = parallaxSlideshowData[slideshowDataID];
if (!slideshowData) return;

Then, it calls a preloadSlides function, which likely preloads images or other resources for the first two slides to prevent a visible delay between slides.

function preloadSlides(slideshowData, slideshow, loadedSlideIds, loadedImages){
    slideshowData.slice(0, 2).forEach((slideData, index) => {
      // Check if the slide has already been added
      if (loadedSlideIds.has(slideData.id)) return;

      // Mark the slide as loaded
      loadedSlideIds.add(slideData.id);

      if (slideData.image !== null) {
        createImageDiv(slideData.id, slideData.image, slideshow, loadedImages, index === 0);
      }
    });
  }

Next, it calls a createImageDiv helper function that is responsible for creating and managing an image element within the parallax slideshow.

function createImageDiv(slideID, slideImage, slideshow, loadedImages, firstImage = false) {
    const imgDiv = document.createElement('div');
    imgDiv.className = 'parallax-slideshow__image';
    imgDiv.innerHTML = slideImage;

    if (firstImage) {
      const image = imgDiv.querySelector('img');
      image.addEventListener('load', () => {
        const slideshowOverlay = slideshow.querySelector(
          '.parallax-slideshow__overlay',
        );
        const slideshowWrapper = slideshow.querySelector(
          '.parallax-slideshow__wrapper',
        );
        if (slideshowOverlay) {
          slideshowOverlay.classList.add('fade-out');
          setTimeout(() => {
            document.body.style.overflow = '';
            slideshowWrapper.removeChild(slideshowOverlay);
          }, 1000);
        }
      });
    }

    // Add a custom attribute for the slide ID
    imgDiv.setAttribute('data-slide-image-id', slideID);

    loadedImages.add({
      id: slideID,
      image: imgDiv,
    });
  }

The reason why we check if there is a firstImage is that we want the initial slide to fade in from black when it's fully loaded. Once the image loads, it finds the overlay and the slideshow wrapper, fades out the overlay, removes the overlay, and re-enables scrolling.

Let's go back to the initializeParallaxSlideshow. After the preloadSlides function there's a scroll event listener for the parallax effect that listens for scroll events to update the slideshow's image position dynamically.

The idea is to let the image wrapper take the whole height of the viewport, but since there can be components before or after the parallax slideshow, at some point it is necessary to change the position of the image wrapper, to let the user scroll and interact with other components.

window.addEventListener('scroll', () => {
 const windowHeight = window.innerHeight;
 const top = slideshow.getBoundingClientRect().top;
 const bottom = slideshow.getBoundingClientRect().bottom;
 const slideshowImageWrapper = slideshow.querySelector(
   '.parallax-slideshow__image-wrapper',
 );
 if (top < 0 && bottom > windowHeight) {
   slideshowImageWrapper.style.position = 'fixed';
   slideshowImageWrapper.style.top = 0;
 } else {
   slideshowImageWrapper.style.position = 'absolute';
   if (windowHeight > bottom) {
     slideshowImageWrapper.style.top = 'unset';
     slideshowImageWrapper.style.bottom = 0;
   }
   if (windowHeight < top) {
    slideshowImageWrapper.style.top = 0;
    slideshowImageWrapper.style.bottom = 'unset';
   }
 }
});

The following logic is to set a scroll hijacking if the parallax slideshow is the first component of the page and if the first slide is an image.

// Check if slideshow is within a parent of .content-top
const isContentTopParent = slideshow.closest('.content-top') !== null;

// Get the first slide and check if it has the class `parallax-slide--parallax-image-slide`
const firstSlide = slideshow.querySelector('.parallax-slide');
const isFirstSlideParallaxImageSlide = firstSlide &&     firstSlide.classList.contains('parallax-slide--parallax-image-slide');

// Lock scroll if .content-top is present and the first slide is of type image
 if (isContentTopParent && isFirstSlideParallaxImageSlide) {
    const overlay = document.createElement('div');
    overlay.className = 'parallax-slideshow__overlay';
    slideshow
      .querySelector('.parallax-slideshow__wrapper')
      .appendChild(overlay);
    document.body.style.overflow = 'hidden';
}

Then, there's a piece of code that iterates through all slides in the slideshow and calls the initializeSlideObserver() function on each slide.

const slides = slideshow.querySelectorAll('.parallax-slide');
    slides.forEach((slide, index) => {
      const infoInnerWrapper = slide.querySelector(
        '.parallax-slide__info-inner-wrapper',
      );
      initializeSlideObserver(slideshow, infoInnerWrapper, slide, slideshowData, loadedSlideIds, loadedImages);
      // Add classes if first image is an slide
      if (index === 0) {
        slide.classList.add('initial-slide');
        if (isFirstSlideParallaxImageSlide) {
          slide.classList.add('initial-slide-image');
        }
      }
    });

Now let's take a look at the initializeSlideObserver() function - the one that is responsible for setting up an Intersection Observer to track when a slide enters the viewport and dynamically updates the slideshow's displayed image accordingly. It ensures that the slideshow loads the next image only when needed, preventing unnecessary rendering and improving performance.

// Initialize Intersection Observer for Slides
  function initializeSlideObserver(slideshow, infoInnerWrapper, slide, slideshowData, loadedSlideIds, loadedImages) {
    // Watches when infoInnerWrapper enters or exits the viewport, 
    // and triggers a callback whenever visibility changes
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          const slideshowWrapper = slideshow.querySelector(
            '.parallax-slideshow__wrapper',
          );
          const slideshowImageWrapper = slideshowWrapper.querySelector(
            '.parallax-slideshow__image-wrapper',
          );
          const slideID = slide.getAttribute('slide-data-id');
          const slideImage = Array.from(loadedImages).find(
            (loadedImage) => loadedImage.id === slideID,
          );

          const { isIntersecting } = entry;

          // Checks if the slide currently intersects the root
          if (isIntersecting) {
            const parent = slide.parentNode;
            const slides = Array.from(
              parent.querySelectorAll('.parallax-slide'),
            );
            const index = slides.indexOf(slide);
            if (index !== 0) {
              // If not first slide, call the function to preload the next slide in advance.
              loadNextSlide(slideshowData, index, loadedSlideIds, loadedImages);
            }

            // Check if there is an existing image
            const previousImage = slideshowImageWrapper.querySelector(
              '.parallax-slideshow__image',
            );

            if (slideImage) {
              slideImage.image.classList.add('fade-in');
              slideshowImageWrapper.appendChild(slideImage.image);

              // If an existing image is found, remove fade-in class and remove it after a delay
              if (previousImage) {
                const previosImageID = previousImage.getAttribute(
                  'data-slide-image-id',
                );

                if (previosImageID !== slideID) {
                  setTimeout(() => {
                    previousImage.classList.add('fade-out'); // Add fade-out class
                    previousImage.classList.remove('fade-in'); // Remove fade-in class
                    previousImage.classList.remove('fade-out'); // Remove fade-out class
                    slideshowImageWrapper.removeChild(previousImage);
                  }, 500);
                }
              }
            } else {
              if (previousImage) {
                const previosImageID = previousImage.getAttribute(
                  'data-slide-image-id',
                );

                if (previosImageID !== slideID) {
                  previousImage.classList.add('fade-out'); // Add fade-out class
                  setTimeout(() => {
                    previousImage.classList.remove('fade-out');
                    slideshowImageWrapper.removeChild(previousImage);
                  }, 500);
                }
              }
            }
          }
        });
      },
      {
        // The callback triggers when at least 5% of infoInnerWrapper is visible.
        threshold: 0.05,
      },
    );

    observer.observe(infoInnerWrapper);
  }

Last but not least, there's the loadNextSlide function that is responsible for preloading the next slide's image to ensure a smooth transition when the user scrolls. This prevents unnecessary reloading of already loaded images. This function is very similar to the preloadSlides function.

function loadNextSlide(slideshowData, currentIndex, loadedSlideIds, loadedImages) {
    if (currentIndex + 1 < slideshowData.length) {
      const nextSlideData = slideshowData[currentIndex + 1];
      // Check if the slide has already been added
      if (loadedSlideIds.has(nextSlideData.id)) return;

      // Mark the slide as loaded
      loadedSlideIds.add(nextSlideData.id);

      if (nextSlideData.image !== null) {
        createImageDiv(nextSlideData.id, nextSlideData.image, null, loadedImages);
      }
    }
  }

With these functions in place - handling image creation, slide observation, and preloading - you now have a dynamic and efficient parallax slideshow that seamlessly transitions between images as users scroll. By leveraging the Intersection Observer API, preloading logic, and smooth fade effects, the slideshow ensures a visually engaging experience without unnecessary performance overhead.

Once you've added the necessary styles to control positioning, animations, and transitions, your parallax slideshow should be fully functional across your site. This approach not only enhances the storytelling aspect of your content, but also keeps interactions smooth and lightweight.

Now, all that's left is to fine-tune the visuals to match your design, and you're set to create an immersive scrolling experience! 🚀

The post Creating depth and motion: A step-by-step guide to parallax appeared first on Four Kitchens.

30 May 2025 2:09pm GMT

The Drop Times: From Drupal Core to Static Site Innovation: Samuel Mortenson on Tome, SFC, and Open Source Legacy

Samuel Mortenson, longtime Drupal core contributor and creator of tools like Tome and SFC, reflects on nearly a decade of shaping the open-source CMS. In this in-depth interview, he shares insights on static site generation, developer experience, and why stepping back doesn't always mean stepping away. If you're curious about the future of Drupal, static-first architecture, or open source sustainability, this is a must-read.

30 May 2025 1:34pm GMT

29 May 2025

feedSymfony Blog

Symfony 7.3 curated new features

Symfony 7.3.0 has been released. As for any other Symfony release, our backward compatibility promise applies and this means that you should be able to upgrade easily to 7.3 without changing anything in your code. During the last couple of months, we've…

29 May 2025 8:13am GMT

Symfony 7.3.0 released

Symfony 7.3.0 has just been released. Check the New in Symfony 7.3 posts on this blog to learn about the main features of this new stable release; or check the first beta release announcement to get the list of all its new features. Read the Symfony upgrade…

29 May 2025 7:51am GMT

16 May 2025

feedPlanet PHP

SQLAlchemy supports Oracle Database 23ai VECTORs

The widely used SQLAlchemy "Python SQL Toolkit and Object Relational Mapper" now supports the Oracle Database 23ai VECTOR data type, enabling your AI applications.

The new support for vectors in SQLAlchemy is courtesy of a code contribution from Suraj Shaw, a member of Oracle Database's driver development group. A big thanks to Mike Bayer and Federico Caselli from the SQLAlchemy team for merging the enhancement into SQLAlchemy 2.0.41.

Rows of wall paint color swatches in different shades of orange. Used here to represent different vector values, and playing on the theme of matching.
Photo by Clay Banks on Unsplash

Vector data in Oracle Database 23ai

Oracle Database 23ai introduced Oracle AI Vector Search as part of the database at no additional charge. Vectors are commonly used in AI to represent the semantics of unstructured data such as images, documents, video, and audio. With the new database feature set, a VECTOR data type was added, being represented as an homogeneous array of 8-bit unsigned integers, 8-bit signed integers, 32-bit floating point numbers, or 64-bit floating point numbers.

Vector columns in Oracle Database 23ai can be created as type:

VECTOR(<vectorDimensions>, <vectorDimensionFormat>)

where the attributes are:

Both the number of dimension and format can be "flexible", allowing vectors in a column to have different shapes.

For example, the SQL to create a table T that includes a VECTOR column called EMBEDDING using FLOAT64 storage is:

CREATE TABLE t (id INTEGER NOT NULL,
name VARCHAR2(20 CHAR),
embedding VECTOR(3,FLOAT64),
PRIMARY KEY (id));

Oracle Database 23.7 introduced the ability to store sparse vectors as an efficient way to represent VECTOR data where most dimensions have zero values. These are not yet supported through SQLAlchemy.

For more information about vectors in the database, refer to the Oracle AI Vector Search User's Guide.

Requirements

To use VECTORs, you need Oracle Database 23ai. Information at oracle.com/database/free will show you how to access a free cloud database or alternatively install one also for free. Vector support is being improved in each database "release update" so use the latest available version.

Next, you need at least version 2.0.41 of sqlalchemy. You should also update oracledb, which itself has been keeping pace with the database's vector improvements:

python -m pip install sqlalchemy oracledb --upgrade

Creating VECTORs in SQLAlchemy

[The full code of example used in this post is available on GitHub here].

In SQLAlchemy, the following creates the same table schema as earlier shown in SQL*Plus:

from sqlalchemy import MetaData, Table, Column, Integer, String
from sqlalchemy.dialects.oracle import VECTOR, VectorStorageFormat

metadata = MetaData()

t = Table(
"t",
metadata,
Column("id", Integer, primary_key=True),
Column("name", String(20)),
Column(
"embedding",
VECTOR(dim=3, storage_format=VectorStorageFormat.FLOAT64),
)
)

Indexes on VECTOR columns can aid performance, see the SQLAlchemy documentation VECTOR Indexes. An example "Inverted File Flat (IVF)" index is:

from sqlalchemy.dialects.oracle import VectorIndexConfig, VectorIndexType, VectorDistanceType

Index(
"iv

Truncated by Planet PHP, read more at the original (another 5671 bytes)

16 May 2025 12:26am GMT

06 May 2025

feedPlanet PHP

Additional parameters on a PHP interface method

On the Roave Discord recently, there was a discussion about not breaking BC in interfaces inspired by this post by Jérôme Tamarelle: It's clearly true that if you add a new parameter to a method on an interface, then that's a BC break as every concrete implementation of the interface needs to change their signature. However, Gina commented that you don't need to use func_get_arg() as concrete implementations can add additional optional arguments. WHAT?!!! I… continue reading.

06 May 2025 10:00am GMT

16 Apr 2025

feedPlanet PHP

Simplifying Python applications by using named connection pools

Python-oracledb 3.0 allows you to name pools when they are created. You can access them later by that name instead of having to pass around a pool handle. This feature is very helpful when your application spans many code files, or consists of independent libraries.

Pools of water in a mountain
Photo by Thomas Gabernig on Unsplash

Multi-user (and some single-user) database applications should almost always use a driver connection pool. This has performance, scalability, and reliability benefits. Check out my previous posts on this topic are A driver connection pool, a DRCP pool, or an Implicit Connection Pool? and Always Use Connection Pools - and How.

But when your application spans multiple files, it can be tricky to pass the pool handle between your code modules. In python-oracledb 3.0 we introduced a driver connection pool cache to simplify your life. You can use the pool cache in both python-oracledb Thin and Thick modes with all the Oracle Database versions that python-oracledb supports. The same cache concept has already proven itself in our node-oracledb driver.

To put pool caching into practice, consider the new code connection_pool_pc.py which is a variant of the sample connection_pool.py. (Follow those links to see the full files).

The original connection_pool.py code creates a pool and returns its handle to the rest of the application:

    pool = oracledb.create_pool(
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
params=sample_env.get_pool_params(),
min=pool_min,
max=pool_max,
increment=pool_inc,
session_callback=init_session,
)

return pool

The new code in connection_pool_pc.py adds a pool_alias=my_pool_alias parameter to create_pool(). It doesn't retain, or use, the pool handle returned by create_pool():

    my_pool_alias = 'mypool'

oracledb.create_pool(
pool_alias=my_pool_alias,
user=sample_env.get_main_user(),
password=sample_env.get_main_password(),
dsn=sample_env.get_connect_string(),
params=sample_env.get_pool_params(),
min=pool_min,
max=pool_max,
increment=pool_inc,
session_callback=init_session,
)

Every time a connection is needed from the pool, the old code:

with pool.acquire() as connection:

is replaced to access the pool directly from the oracledb module:

with oracledb.connect(pool_alias=my_pool_alias) as connection:

The full diff between the files is:

71a72,73
> my_pool_alias = 'mypool'
>
88c90,91
< pool = oracledb.create_pool(
---
> oracledb.create_pool(
> pool_alias=my_pool_alias,
99d101
< return pool
101d102
<
128c129
< with pool.acquire() as connection:
---
> with oracledb.connect(pool_alias=my_pool_alias) as connection:
172c173
< with pool.acquire() as connection:
---
> with oracledb.connect(pool_alias=my_pool_alias) as connection:
190c191
< with pool.acquire() as connection:
---
> with oracledb.connect(pool_alias=my_pool_alias) as connection:
201c202
< pool = start_pool()
---
> start_pool()

The files run identically.

The benefit of pool caching is that modules and libraries that access a pool only need to agree on a name (or names - if you have multiple pools). After importing oracledb, each part of the code can access a pool directly off the imported oracledb module by using the agreed name.

You can also pass options to oracledb.connect() that you might have previously passed to pool.acquire(). The documented example is when you are using a heterogeneous pool where each connection could be a different user. In

Truncated by Planet PHP, read more at the original (another 1879 bytes)

16 Apr 2025 9:31pm GMT